using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Forms;
using System.Xml;
using CodeLines.CodeIgnoreRules;
using PinFun.Core.Utils;
using PinFun.Wpf;
using PinFun.Wpf.Controls;
using SharpDocx;
// ReSharper disable LocalizableElement

namespace CodeLines
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : IUseViewModel<MainWindowModel>
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        #region 添加目录

        private void ChooseDir_Click(object sender, RoutedEventArgs e)
        {
            var folder = new FolderBrowserDialog();
            if (folder.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                if (ViewModel.Dirs.Contains(folder.SelectedPath)) return;
                ViewModel.Dirs.Add(folder.SelectedPath);
            }
        }

        #endregion

        #region 开始

        private async void Begin_OnClick(object sender, RoutedEventArgs e)
        {
            if (ViewModel.Dirs.Count == 0)
            {
                ShowToast($"请添加需要统计的目录");
                return;
            }

            ViewModel.ActiveTabIndex = 1;

            var exts = (ViewModel.Ext ?? "").ToLower().Split('|', StringSplitOptions.RemoveEmptyEntries);
            var newExts = new List<string>();
            foreach (var ext in exts)
            {
                var part = ext.Split('.', StringSplitOptions.RemoveEmptyEntries);
                if (part.Length == 1)
                {
                    newExts.Add($".{part[0]}");
                }
                else if (part.Length == 2)
                {
                    newExts.Add($".{part[1]}");
                }
                else
                {
                    ShowToast($"错误的后缀：{ext}");
                }
            }

            if (newExts.Count == 0)
            {
                ShowToast("请输入需要统计的文件后缀", MessageLevel.Waring);
                return;
            }

            try
            {
                ViewModel.CanClick = false;
                ViewModel.Info = I18N("正在读取目录清单...");

                var ignoreDir = (ViewModel.Ignore ?? "").ToLower().Split('|', StringSplitOptions.RemoveEmptyEntries);

                var allDir = await Task.Run(() => GetAllDir(ignoreDir));

                ViewModel.Info = I18N("正在扫描文件列表...");
                var files = await GetFiles(allDir, newExts.ToArray());

                ViewModel.Info = I18N("正在扫描代码行数...");
                var newArray = new List<string>();
                foreach (var ext in newExts)
                {
                    newArray.AddRange(files
                        .Where(a => a.EndsWith(ext))
                        .OrderBy(Path.GetFileNameWithoutExtension));
                }

                await Run(newArray.ToArray());

                var total = ViewModel.Files.Sum(c => c.Code);
                ViewModel.CodeSummary = I18N("扫描目录总数:{0}；文件总数:{1}；代码总行数:{2}")
                    .Format(allDir.Length, ViewModel.Files.Count, total);
                ViewModel.Info = I18N("统计完成.");
            }
            catch (Exception ex)
            {
                ShowToast(ex.Message, MessageLevel.Error);
            }
            finally
            {
                ViewModel.CanClick = true;
            }
        }

        #endregion

        #region 获取目录

        string[] GetAllDir(string[] ignoreDir)
        {
            var dirs = ViewModel.Dirs.ToArray();
            var tasks = new List<Task<string[]>>();
            var newDirs = new List<string>();
            foreach (var dir in dirs)
            {
                if (IsInIgnoreDir(dir, ignoreDir)) continue;
                newDirs.Add(dir);
                tasks.Add(ReadSubDir(dir, ignoreDir));
            }

            // ReSharper disable once CoVariantArrayConversion
            Task.WaitAll(tasks.ToArray());

            foreach (var task in tasks)
            {
                if (task.Result.Length == 0) continue;
                newDirs.AddRange(task.Result);
            }

            return newDirs.ToArray();
        }

        bool IsInIgnoreDir(string dir, string[] ignores)
        {
            var dirInfo = new DirectoryInfo(dir);
            return ignores.Contains(dirInfo.Name.ToLower());
        }

        async Task<string[]> ReadSubDir(string dir, string[] ignoreDir)
        {
            var subDirs = await Task.Run(() => Directory.GetDirectories(dir));
            var mySubs = new List<string>();
            var tasks = new List<Task<string[]>>();
            foreach (var subDir in subDirs)
            {
                if (IsInIgnoreDir(dir, ignoreDir)) continue;
                mySubs.Add(subDir);
                tasks.Add(ReadSubDir(subDir, ignoreDir));
            }

            await Task.WhenAll(tasks);

            var newDirs = new List<string>();
            newDirs.AddRange(mySubs);
            foreach (var task in tasks)
            {
                if (task.Result.Length == 0) continue;
                newDirs.AddRange(task.Result);
            }

            return newDirs.ToArray();
        }

        #endregion

        #region 执行统计

        async Task Run(string[] files)
        {
            ViewModel.Files.Clear();
            foreach (var file in files)
            {
                ViewModel.Files.Add(new CodeFile(file));
            }

            var allTask = new List<Task>();
            foreach (var file in ViewModel.Files)
            {
                allTask.Add(file.ReadLineCount());
            }

            await Task.WhenAll(allTask);
        }

        #endregion

        #region 获取文件清单

        async Task<string[]> GetFiles(string[] dirs, string[] exts)
        {
            var allTask = new List<Task<string[]>>();
            foreach (var dir in dirs)
            {
                allTask.Add(ScanFiles(dir, exts));
            }

            await Task.WhenAll(allTask);

            var files = new List<string>();
            foreach (var task in allTask)
            {
                var f = task.Result;
                if (f.Length == 0) continue;
                files.AddRange(f);
            }

            return files.ToArray();
        }

        Task<string[]> ScanFiles(string dir, string[] exts)
        {
            return Task.Run(() =>
            {
                var files = new List<string>();
                var filePaths = Directory.GetFiles(dir, "*.*", SearchOption.TopDirectoryOnly);
                foreach (var file in filePaths)
                {
                    var ext = Path.GetExtension(file).ToLower();
                    if (!exts.Contains(ext)) continue;
                    files.Add(file);
                }

                return files.ToArray();
            });
        }

        #endregion

        #region 导出

        private async void Export_OnClick(object sender, RoutedEventArgs e)
        {
            if (ViewModel.Files.Count == 0) return;
            ViewModel.Info = "正在导出...";
            ViewModel.CanClick = false;
            var isSuccessed = await Generate();
            if (isSuccessed)
            {
                var sfd = new SaveFileDialog()
                {
                    Title = "另存为",
                    Filter = "Word文件|*.docx",
                    InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop)
                };
                if (sfd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                {
                    if (File.Exists(sfd.FileName)) File.Delete(sfd.FileName);
                    File.Move("output.docx", sfd.FileName);
                }

                ViewModel.Info = "导出成功.";
                ShowToast("文件导出成功！");
            }

            ViewModel.CanClick = true;
        }

        IEnumerable<ICodeIgnoreRule> GetIgnoreRule()
        {
            using var strReader = new StringReader(ViewModel.IgnoreCodes);
            while (strReader.ReadLine() is { } line)
            {
                var contain = new Contain();
                if (contain.SetRule(line))
                {
                    yield return contain;
                    continue;
                }

                var startWith = new StartWith();
                if (startWith.SetRule(line))
                {
                    yield return startWith;
                    continue;
                }

                var endWith = new EndWith();
                if (endWith.SetRule(line))
                {
                    yield return endWith;
                }
            }
        }

        Task<bool> Generate()
        {
            return Task.Run(() =>
            {
                try
                {
                    var codes = new List<string>();
                    var rules = GetIgnoreRule().ToArray();
                    var totalLine = ViewModel.LineCount * ViewModel.PageCount;
                    foreach (var file in ViewModel.Files)
                    {
                        var allLines = FilterInvalidXmlChar(File.ReadAllLines(file.FullPath));
                        foreach (var line in allLines)
                        {
                            var skip = false;
                            foreach (var rule in rules)
                            {
                                skip = skip || rule.IsIgnore(line);
                            }

                            if (skip) continue;
                            codes.Add(line);
                            if (codes.Count > totalLine) break;
                        }

                        if (codes.Count > totalLine) break;
                    }

                    if (File.Exists("code.cs.docx"))
                    {
                        File.Delete("code.cs.docx");
                    }

                    File.WriteAllBytes("code.cs.docx", Properties.Resources.code_cs);
                    var document = DocumentFactory.Create("code.cs.docx", new DocxModel
                    {
                        Codes = codes.Select(XmlConvert.EncodeName).ToArray()
                    });
                    if (File.Exists("output.docx")) File.Delete("output.docx");
                    document.Generate("output.docx", new DocxModel { Codes = codes.ToArray() });
                    File.Delete("code.cs.docx");

                    return true;
                }
                catch (Exception ex)
                {
                    ViewModel.Info = ex.Message;
                    return false;
                }
            });
        }

        IEnumerable<string> FilterInvalidXmlChar(string[] input)
        {
            foreach (var s in input)
            {
                var line = s.Trim();
                if (string.IsNullOrWhiteSpace(line)) continue;
                var isOk = true;
                foreach (var c in line)
                {
                    isOk = isOk && IsInvalidXmlChar(c);
                }

                if (isOk) yield return line;
            }
        }

        bool IsInvalidXmlChar(char c)
        {
            return (
                c == '\x9' ||
                c == '\xA' ||
                c == '\xD' ||
                (c >= '\x20' && c <= '\uD7FF') ||
                (c >= '\uE000' && c <= '\uFFFD')
            );
            // 注：char 类型无法表示 \u10000 到 \u10FFFF，这些是高代理项，需额外处理
        }

        #endregion

        public MainWindowModel ViewModel => GetViewModel<MainWindowModel>();
    }
}
